package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Log4j2
@Service(value = "IN_WATCH_LIST_POINT_RULE")
@RequiredArgsConstructor
public class InWatchListRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "CompanyWatch";
    private List<InWatchListParam> params;
    private List<String> companyList = new ArrayList<>();

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        return Flux.just()
                .doFinally(r -> {
                    this.params = super.objectMapper
                            .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(), new TypeReference<>() {
                            });
                    String inWatchCompanies = gradeRuleInterceptorRegistration.getRule().getRequest().getInWatchCompanies();
                    this.companyList = Arrays.stream(StringUtils.commaDelimitedListToStringArray(inWatchCompanies))
                            .collect(Collectors.toList());
                })
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        String companyName = super.getStringValueName(ticket.get("company"));
        int order = 0;
        if (!CollectionUtils.isEmpty(this.companyList) && StringUtils.hasLength(companyName)) {
            order = params.parallelStream().filter(InWatchListParam::isSelected)
                    .filter(item -> this.companyList.contains(companyName.toLowerCase()))
                    .mapToInt(InWatchListParam::getPoints).sum();
        }
        return Mono.just(PointResult.withDefault().point(order)
                .detail(PointResult.Detail.with(order,
                        Map.of("companyName", companyName), RULE_KEY)));
    }

    @Data
    private static class InWatchListParam {
        private Integer points;
        private boolean selected;
        private Integer index;
        private String condition;
    }
}
